package com.yonghui.summer.ioc.context.impl;

import android.app.Activity;
import android.content.Context;

import com.yonghui.summer.annotation.DaoComponent;
import com.yonghui.summer.ioc.annotation.Configuration;
import com.yonghui.summer.ioc.annotation.Controller;
import com.yonghui.summer.ioc.annotation.OfflineApplication;
import com.yonghui.summer.ioc.annotation.RQualifier;
import com.yonghui.summer.ioc.context.BootContext;
import com.yonghui.summer.ioc.environment.Environment;
import com.yonghui.summer.ioc.factory.SingletonRegistry;
import com.yonghui.summer.ioc.scanner.DexFileScanner;
import com.yonghui.summer.ioc.scanner.impl.DexFileScannerImpl;
import com.yonghui.summer.ioc.utils.ReflectUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 标准的BootContext
 */
@Slf4j
public class StandardBootContext implements BootContext, Environment {

    /**
     * 主类
     */
    private Activity activity;

    public StandardBootContext(Activity activity) {
        this.activity = activity;
        loadBeanWithActity();
    }

    public StandardBootContext(Context context,String[] packages) {
        scan(context,packages);
        handleInit();
    }


    @Override
    public <T> T getBean(Integer rId, Class<T> tClass) {
        return (T) SingletonRegistry.getInstance().getBeanFactory(activity, rId, tClass);
    }

    @Override
    public Object getBean(String name) {
        return SingletonRegistry.getInstance().getBeanFactory(name);
    }

    @Override
    public <T> T getBean(Class<T> name) {
        return (T) SingletonRegistry.getInstance().getBeansOfType(name);
    }

    @Override
    public <T> T getBean(String name, Class<T> tClass) {
        return (T) SingletonRegistry.getInstance().getBeanFactory(name);
    }

    @Override
    public void registerBean(Integer rId, Class tClass) {
        SingletonRegistry.getInstance().getBeanFactory(activity, rId, tClass);
    }

    @Override
    public void registerBean(Object bean) {
        SingletonRegistry.getInstance().getBeanFactory(bean.getClass().getName(), bean);
    }

    @Override
    public void registerBean(String name, Object bean) {
        SingletonRegistry.getInstance().getBeanFactory(name, bean);
    }

    @Override
    public void registerBean(String name, Class bean) {
        SingletonRegistry.getInstance().getBeanFactory(name, bean);
    }

    @Override
    public void registerBean(Class clazz) {
        SingletonRegistry.getInstance().getBeanFactory(clazz.getName());
    }

    @Override
    public Map<String, Object> getSystemProperties() {
        //TODO 获得系统属性
        return null;
    }

    @Override
    public Map<String, Object> getSystemEnvironment() {
        // TODO 获得系统环境
        return null;
    }

    public Activity getActivity() {
        return activity;
    }

    public void setActivity(Activity activity) {
        this.activity = activity;
    }

    @Override
    public Context getContext() {
        return activity.getApplicationContext();
    }

    public void loadBeanWithActity() {
        Class clazz = activity.getClass();
        OfflineApplication applicationInfo = null;
        if (clazz.isAnnotationPresent(OfflineApplication.class)) {
            applicationInfo = (OfflineApplication) clazz.getAnnotation(OfflineApplication.class);
        }
        if (applicationInfo == null) {
            return;
        }
        String[] packages = applicationInfo.basePackages();
        if (packages == null || packages.length == 0) {
            if (applicationInfo.autoConfiguration()) {
                packages = new String[]{activity.getPackageName()};
            }
        }
        scan(activity,packages);

        this.handleAutowired(clazz, activity);
    }


    @Override
    public void scan(Context context,String[] packages) {
        DexFileScanner dexFileScanner = new DexFileScannerImpl();
        dexFileScanner.setContext(context);
        Set<String> strings = dexFileScanner.doScan(packages);
        for (String s : strings) {
            if(s.contains("$")){
                continue;
            }
            System.out.println("load class:{}"+s);
            try {
                Class scanClass = Class.forName(s,false,this.getClass().getClassLoader());
                this.register(scanClass);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
        for (String s : strings) {
            if(s.contains("$")){
                continue;
            }
            System.out.println("autowired beand:{}"+s);
            try {
                Class scanClass = Class.forName(s,false,this.getClass().getClassLoader());
                Object src = this.getSrc(scanClass);
                this.handleAutowired(scanClass, src);
            } catch (Exception e) {
                e.printStackTrace();
                continue;
            }
        }
    }

    @Override
    public void handleAutowired( Activity activity){
        this.handleAutowired(activity.getClass(),activity);
    }

    /**
     * 从map中获取bean
     * @param clazz
     * @return
     */
    private Object getSrc(Class clazz) {
        if(clazz.isInterface()){
            return null;
        }
        if (clazz.isAnnotationPresent(Service.class)) {
            Service service = (Service) clazz.getAnnotation(Service.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                return this.getBean(clazz);
            } else {
                return this.getBean(service.value(), clazz);
            }
        }

        if (clazz.isAnnotationPresent(Component.class)) {
            Component component = (Component) clazz.getAnnotation(Component.class);
            if (component.value() == null || "".equals(component.value().trim())) {
                return this.getBean(clazz);
            } else {
                return this.getBean(component.value(), clazz);
            }
        }


        if (clazz.isAnnotationPresent(DaoComponent.class)) {
            DaoComponent component = (DaoComponent) clazz.getAnnotation(DaoComponent.class);
            if (component.value() == null || "".equals(component.value().trim())) {
                return this.getBean(clazz);
            } else {
                return this.getBean(component.value(), clazz);
            }
        }

        if (clazz.isAnnotationPresent(Configuration.class)) {
            Configuration configuration = (Configuration) clazz.getAnnotation(Configuration.class);
            if (configuration.value() == null || "".equals(configuration.value().trim())) {
                return this.getBean(clazz);
            } else {
                return this.getBean(configuration.value(), clazz);
            }
        }

        if (clazz.isAnnotationPresent(Controller.class)) {
            Controller controller = (Controller) clazz.getAnnotation(Controller.class);
            if (controller.value() == null || "".equals(controller.value().trim())) {
                return this.getBean(clazz);
            } else {
                return this.getBean(controller.value(), clazz);
            }
        }
        return null;
    }

    /**
     * 注册bean 到全局的factroyMap容器中
     * @param clazz
     */
    private void register(Class clazz) {
        if(clazz.isInterface()){
            return;
        }

        if (clazz.isAnnotationPresent(Service.class)) {
            Service service = (Service) clazz.getAnnotation(Service.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                this.registerBean(clazz);
                return;
            } else {
                this.registerBean(service.value(), clazz);
                return;
            }
        }

        if (clazz.isAnnotationPresent(Component.class)) {
            Component service = (Component) clazz.getAnnotation(Component.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                this.registerBean(clazz);
                return;
            } else {
                this.registerBean(service.value(), clazz);
                return;
            }
        }


        if (clazz.isAnnotationPresent(DaoComponent.class)) {
            DaoComponent service = (DaoComponent) clazz.getAnnotation(DaoComponent.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                this.registerBean(clazz);
                return;
            } else {
                this.registerBean(service.value(), clazz);
                return;
            }
        }

        if (clazz.isAnnotationPresent(Configuration.class)) {
            Configuration service = (Configuration) clazz.getAnnotation(Configuration.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                this.registerBean(clazz);
                return;
            } else {
                this.registerBean(service.value(), clazz);
                return;
            }
        }

        if (clazz.isAnnotationPresent(Controller.class)) {
            Controller service = (Controller) clazz.getAnnotation(Controller.class);
            if (service.value() == null || "".equals(service.value().trim())) {
                this.registerBean(clazz);
                return;
            } else {
                this.registerBean(service.value(), clazz);
                return;
            }
        }
    }

    /**
     *
     */
    private void handleInit(){
        List<Object> items = SingletonRegistry.getInstance().getBeans();
        for(Object obj : items) {
            System.out.println("init bean:"+obj.getClass().getName());
            Method[] methods = obj.getClass().getMethods();
            if (methods != null) {
                for (Method method : methods) {
                    if (method.isAnnotationPresent(PostConstruct.class)) {
                        try {
                            method.invoke(obj);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }

    /**
     * 将bwan注入到对象
     * @param clazz
     * @param src
     */
    @Override
    public void handleAutowired(Class clazz, Object src) {
        if (src == null) {
            return;
        }
        Set<Field> fields = ReflectUtils.getClassSet(clazz);
        for (Field field : fields) {
            Class t = field.getType();
            Autowired autowired = null;
            if (field.isAnnotationPresent(Autowired.class)) {
                autowired = field.getAnnotation(Autowired.class);
            }
            if (autowired == null) {
                continue;
            }
            String qualifier = null;
            if (field.isAnnotationPresent(Qualifier.class)) {
                qualifier = field.getAnnotation(Qualifier.class).value();
            }

            if (qualifier == null) {
                if (field.isAnnotationPresent(RQualifier.class)) {
                    int[] ids = field.getAnnotation(RQualifier.class).value();
                    Object o = this.getBean(ids[0], t);
                    ReflectUtils.setData(field, src, o);
                    continue;
                }
            }

            if (qualifier == null) {
                Object data = this.getBean(t);
                ReflectUtils.setData(field, src, data);
            }else{
                Object data = this.getBean(qualifier);
                ReflectUtils.setData(field, src, data);
            }
        }
        if(clazz.getSuperclass().isInterface()){
            return;
        }
        if(clazz.getSuperclass().equals(Object.class)){
            return;
        }
        handleAutowired(clazz.getSuperclass(),src);
    }
}
